﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Linq;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using IPC.Communication.BasicFramework;
using IPC.Communication.Core;
using IPC.Communication.Core.IMessage;
using IPC.Communication.Core.Net;
using IPC.Communication.Reflection;

namespace IPC.Communication.Profinet.Omron
{
    public class OmronHostLinkOverTcp : NetworkDeviceBase
    {
        private byte _icf = 0;
        private byte _da2 = 0;
        private byte _sid = 0;
        private byte _responseWaitTime = 48;
        private int _readSplits = 260;
        public byte ICF
        {
            get { return _icf; }
            set { _icf = value; }
        }


        public byte DA2
        {
            get { return _da2; }
            set { _da2 = value; }
        }


        public byte SA2
        {
            get;
            set;
        }

        public byte SID
        {
            get { return _sid; }
            set { _sid = value; }
        }


        public byte ResponseWaitTime
        {
            get { return _responseWaitTime; }
            set { _responseWaitTime = value; }
        }


        public byte UnitNumber
        {
            get;
            set;
        }

        public int ReadSplits
        {
            get { return _readSplits; }
            set { _readSplits = value; }
        }


        public OmronHostLinkOverTcp()
        {
            base.ByteTransform = new ReverseWordTransform();
            base.WordLength = 1;
            base.ByteTransform.DataFormat = DataFormat.CDAB;
            LogMsgFormatBinary = false;
        }

        public OmronHostLinkOverTcp(string ipAddress, int port)
            : this()
        {
            IpAddress = ipAddress;
            Port = port;
        }

        protected override OperateResult<byte[]> UnpackResponseContent(byte[] send, byte[] response)
        {
            return ResponseValidAnalysis(send, response);
        }

        protected override OperateResult<byte[]> ReceiveByMessage(Socket socket, int timeOut, INetMessage netMessage, Action<long, long> reportProgress = null)
        {
            return ReceiveCommandLineFromSocket(socket, 13, timeOut);
        }

        //protected override Task<OperateResult<byte[]>> ReceiveByMessageAsync(Socket socket, int timeOut, INetMessage netMessage, Action<long, long> reportProgress = null)
        //{
        //    return ReceiveCommandLineFromSocketAsync(socket, 13, timeOut);
        //}

        [HslMqttApi("ReadByteArray", "")]
        public override OperateResult<byte[]> Read(string address, ushort length)
        {
            byte station = (byte)HslHelper.ExtractParameter(ref address, "s", UnitNumber);
            bool isBit = false;
            OperateResult<List<byte[]>> operateResult = OmronFinsNetHelper.BuildReadCommand(address, length, isBit = false, ReadSplits);
            if (!operateResult.IsSuccess)
            {
                return operateResult.ConvertFailed<byte[]>();
            }
            List<byte> list = new List<byte>();
            for (int i = 0; i < operateResult.Content.Count; i++)
            {
                OperateResult<byte[]> operateResult2 = ReadFromCoreServer(PackCommand(station, operateResult.Content[i]));
                if (!operateResult2.IsSuccess)
                {
                    return OperateResult.CreateFailedResult<byte[]>(operateResult2);
                }
                list.AddRange(operateResult2.Content);
            }
            return OperateResult.CreateSuccessResult(list.ToArray());
        }

        [HslMqttApi("WriteByteArray", "")]
        public override OperateResult Write(string address, byte[] value)
        {
            byte station = (byte)HslHelper.ExtractParameter(ref address, "s", UnitNumber);
            OperateResult<byte[]> operateResult = OmronFinsNetHelper.BuildWriteWordCommand(address, value, isBit: false);
            if (!operateResult.IsSuccess)
            {
                return operateResult;
            }
            OperateResult<byte[]> operateResult2 = ReadFromCoreServer(PackCommand(station, operateResult.Content));
            if (!operateResult2.IsSuccess)
            {
                return operateResult2;
            }
            return OperateResult.CreateSuccessResult();
        }

        //[AsyncStateMachine(typeof(<ReadAsync>d__35))]
        //[DebuggerStepThrough]
        //public override Task<OperateResult<byte[]>> ReadAsync(string address, ushort length)
        //{
        //    <ReadAsync>d__35 stateMachine = new <ReadAsync>d__35();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult<byte[]>>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.address = address;
        //    stateMachine.length = length;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        //[AsyncStateMachine(typeof(<WriteAsync>d__36))]
        //[DebuggerStepThrough]
        //public override Task<OperateResult> WriteAsync(string address, byte[] value)
        //{
        //    <WriteAsync>d__36 stateMachine = new <WriteAsync>d__36();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.address = address;
        //    stateMachine.value = value;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        [HslMqttApi("ReadBoolArray", "")]
        public override OperateResult<bool[]> ReadBool(string address, ushort length)
        {
            byte station = (byte)HslHelper.ExtractParameter(ref address, "s", UnitNumber);
            OperateResult<List<byte[]>> operateResult = OmronFinsNetHelper.BuildReadCommand(address, length, isBit: true);
            if (!operateResult.IsSuccess)
            {
                return OperateResult.CreateFailedResult<bool[]>(operateResult);
            }
            List<bool> list = new List<bool>();
            for (int i = 0; i < operateResult.Content.Count; i++)
            {
                OperateResult<byte[]> operateResult2 = ReadFromCoreServer(PackCommand(station, operateResult.Content[i]));
                if (!operateResult2.IsSuccess)
                {
                    return OperateResult.CreateFailedResult<bool[]>(operateResult2);
                }
                if (operateResult2.Content.Length == 0)
                {
                    return new OperateResult<bool[]>("Data is empty.");
                }
                list.AddRange(operateResult2.Content.Select((byte m) => m != 0));
            }
            return OperateResult.CreateSuccessResult(list.ToArray());
        }

        [HslMqttApi("WriteBoolArray", "")]
        public override OperateResult Write(string address, bool[] values)
        {
            byte station = (byte)HslHelper.ExtractParameter(ref address, "s", UnitNumber);
            OperateResult<byte[]> operateResult = OmronFinsNetHelper.BuildWriteWordCommand(address, values.Select((bool m) => (byte)(m ? 1 : 0)).ToArray(), isBit: true);
            if (!operateResult.IsSuccess)
            {
                return operateResult;
            }
            OperateResult<byte[]> operateResult2 = ReadFromCoreServer(PackCommand(station, operateResult.Content));
            if (!operateResult2.IsSuccess)
            {
                return operateResult2;
            }
            return OperateResult.CreateSuccessResult();
        }

        //[AsyncStateMachine(typeof(<ReadBoolAsync>d__39))]
        //[DebuggerStepThrough]
        //public override Task<OperateResult<bool[]>> ReadBoolAsync(string address, ushort length)
        //{
        //    <ReadBoolAsync>d__39 stateMachine = new <ReadBoolAsync>d__39();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult<bool[]>>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.address = address;
        //    stateMachine.length = length;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        //[AsyncStateMachine(typeof(<WriteAsync>d__40))]
        //[DebuggerStepThrough]
        //public override Task<OperateResult> WriteAsync(string address, bool[] values)
        //{
        //    <WriteAsync>d__40 stateMachine = new <WriteAsync>d__40();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.address = address;
        //    stateMachine.values = values;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        public override string ToString()
        {
            return String.Format("OmronHostLinkOverTcp[{0}:{1}]", IpAddress, Port);
        }

        private byte[] PackCommand(byte station, byte[] cmd)
        {
            cmd = SoftBasic.BytesToAsciiBytes(cmd);
            byte[] array = new byte[18 + cmd.Length];
            array[0] = 64;
            array[1] = SoftBasic.BuildAsciiBytesFrom(station)[0];
            array[2] = SoftBasic.BuildAsciiBytesFrom(station)[1];
            array[3] = 70;
            array[4] = 65;
            array[5] = ResponseWaitTime;
            array[6] = SoftBasic.BuildAsciiBytesFrom(ICF)[0];
            array[7] = SoftBasic.BuildAsciiBytesFrom(ICF)[1];
            array[8] = SoftBasic.BuildAsciiBytesFrom(DA2)[0];
            array[9] = SoftBasic.BuildAsciiBytesFrom(DA2)[1];
            array[10] = SoftBasic.BuildAsciiBytesFrom(SA2)[0];
            array[11] = SoftBasic.BuildAsciiBytesFrom(SA2)[1];
            array[12] = SoftBasic.BuildAsciiBytesFrom(SID)[0];
            array[13] = SoftBasic.BuildAsciiBytesFrom(SID)[1];
            array[array.Length - 2] = 42;
            array[array.Length - 1] = 13;
            cmd.CopyTo(array, 14);
            int num = array[0];
            for (int i = 1; i < array.Length - 4; i++)
            {
                num ^= array[i];
            }
            array[array.Length - 4] = SoftBasic.BuildAsciiBytesFrom((byte)num)[0];
            array[array.Length - 3] = SoftBasic.BuildAsciiBytesFrom((byte)num)[1];
            return array;
        }

        public static OperateResult<byte[]> ResponseValidAnalysis(byte[] send, byte[] response)
        {
            if (response.Length >= 27)
            {
                string @string = Encoding.ASCII.GetString(send, 14, 4);
                string string2 = Encoding.ASCII.GetString(response, 15, 4);
                if (string2 != @string)
                {
                    return new OperateResult<byte[]>("Send Command [" + @string + "] not the same as receive command [" + string2 + "]");
                }
                int num = Convert.ToInt32(Encoding.ASCII.GetString(response, 19, 4), 16);
                byte[] array = new byte[0];
                if (response.Length > 27)
                {
                    array = SoftBasic.HexStringToBytes(Encoding.ASCII.GetString(response, 23, response.Length - 27));
                }
                if (num > 0)
                {
                    return new OperateResult<byte[]>
                    {
                        ErrorCode = num,
                        Content = array,
                        Message = GetErrorText(num)
                    };
                }
                return OperateResult.CreateSuccessResult(array);
            }
            return new OperateResult<byte[]>(StringResources.Language.OmronReceiveDataError + " Source Data: " + response.ToHexString(' '));
        }

        public static string GetErrorText(int error)
        {
            switch (error)
            {
                case 1:
                    return "Service was canceled.";
                case 257:
                    return "Local node is not participating in the network.";
                case 258:
                    return "Token does not arrive.";
                case 259:
                    return "Send was not possible during the specified number of retries.";
                case 260:
                    return "Cannot send because maximum number of event frames exceeded.";
                case 261:
                    return "Node address setting error occurred.";
                case 262:
                    return "The same node address has been set twice in the same network.";
                case 513:
                    return "The destination node is not in the network.";
                case 514:
                    return "There is no Unit with the specified unit address.";
                case 515:
                    return "The third node does not exist.";
                case 516:
                    return "The destination node is busy.";
                case 517:
                    return "The message was destroyed by noise";
                case 769:
                    return "An error occurred in the communications controller.";
                case 770:
                    return "A CPU error occurred in the destination CPU Unit.";
                case 771:
                    return "A response was not returned because an error occurred in the Board.";
                case 772:
                    return "The unit number was set incorrectly";
                case 1025:
                    return "The Unit/Board does not support the specified command code.";
                case 1026:
                    return "The command cannot be executed because the model or version is incorrect";
                case 1281:
                    return "The destination network or node address is not set in the routing tables.";
                case 1282:
                    return "Relaying is not possible because there are no routing tables";
                case 1283:
                    return "There is an error in the routing tables.";
                case 1284:
                    return "An attempt was made to send to a network that was over 3 networks away";
                case 4097:
                    return "The command is longer than the maximum permissible length.";
                case 4098:
                    return "The command is shorter than the minimum permissible length.";
                case 4099:
                    return "The designated number of elements differs from the number of write data items.";
                case 4100:
                    return "An incorrect format was used.";
                case 4101:
                    return "Either the relay table in the local node or the local network table in the relay node is incorrect.";
                case 4353:
                    return "The specified word does not exist in the memory area or there is no EM Area.";
                case 4354:
                    return "The access size specification is incorrect or an odd word address is specified.";
                case 4355:
                    return "The start address in command process is beyond the accessible area";
                case 4356:
                    return "The end address in command process is beyond the accessible area.";
                case 4358:
                    return "FFFF hex was not specified.";
                case 4361:
                    return "A large–small relationship in the elements in the command data is incorrect.";
                case 4363:
                    return "The response format is longer than the maximum permissible length.";
                case 4364:
                    return "There is an error in one of the parameter settings.";
                case 8194:
                    return "The program area is protected.";
                case 8195:
                    return "A table has not been registered.";
                case 8196:
                    return "The search data does not exist.";
                case 8197:
                    return "A non-existing program number has been specified.";
                case 8198:
                    return "The file does not exist at the specified file device.";
                case 8199:
                    return "A data being compared is not the same.";
                case 8449:
                    return "The specified area is read-only.";
                case 8450:
                    return "The program area is protected.";
                case 8451:
                    return "The file cannot be created because the limit has been exceeded.";
                case 8453:
                    return "A non-existing program number has been specified.";
                case 8454:
                    return "The file does not exist at the specified file device.";
                case 8455:
                    return "A file with the same name already exists in the specified file device.";
                case 8456:
                    return "The change cannot be made because doing so would create a problem.";
                case 8705:
                case 8706:
                case 8712:
                    return "The mode is incorrect.";
                case 8707:
                    return "The PLC is in PROGRAM mode.";
                case 8708:
                    return "The PLC is in DEBUG mode.";
                case 8709:
                    return "The PLC is in MONITOR mode.";
                case 8710:
                    return "The PLC is in RUN mode.";
                case 8711:
                    return "The specified node is not the polling node.";
                case 8961:
                    return "The specified memory does not exist as a file device.";
                case 8962:
                    return "There is no file memory.";
                case 8963:
                    return "There is no clock.";
                case 9217:
                    return "The data link tables have not been registered or they contain an error.";
                default:
                    return StringResources.Language.UnknownError;
            }
        }
    }
}
